from datetime import datetime
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from django.conf import settings
from django.contrib.auth import authenticate, login
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from rest_framework.response import Response
from rest_framework.decorators import action
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from django.contrib.auth.hashers import make_password
import json
import logging
import re

#  import owner
from system.permissions import has_role_permission,IsOwnerOnly,PermissionDenied, MenuBasePermisson, \
                                has_role_permission_func, has_role_permission_decorator
from system.filters import UserFilter,RoleFilter,RoleMenuFilter
from system.models import SysUser,SysRole,SysRoleMenu,SysMenu,SysUserRole,SysConfig,SysOperLog
from system.serializers import SysUserSerializer,SysRoleSerializer,SysConfigSerializer, \
                               SysMenuSerializer,SysRoleMenuSerializer,BatchDeleteSerializer, \
                               MessageResponseSerializer,GrantMenuSerializer, SysOperLogSerializer
from utils.CommonUtils import CommonPagination, build_tree, LdapEntity, get_object, PyCrypt, filter_button_menus
logger = logging.getLogger('ywadmin')


from django.urls import get_resolver
from django.http import JsonResponse

# Create views here.
class LoginView(APIView):
    """
    用户登录
    
    提供用户登录功能，返回JWT令牌和用户信息
    """
    permission_classes = []
    
    def _get_client_ip(self, request):
        """获取客户端IP地址"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip
    
    def _check_login_limit(self, username, ip):
        """检查登录限制"""
        from system.models import SysLimit
        from django.utils import timezone
        
        # 获取配置的最大重试次数
        basic_config = get_object(SysConfig, name='basic')
        max_retry = 5  # 默认最大重试次数
        if basic_config and basic_config.json_value.get('passwordErrorRetry'):
            max_retry = int(basic_config.json_value.get('passwordErrorRetry', 5))
        
        # 查询该用户名的登录限制记录
        limit_record = SysLimit.objects.filter(
            username=username,
            limit_key='login'
        ).first()
        
        if limit_record:
            # 检查是否被锁定
            if limit_record.is_locked and limit_record.locked_until:
                if timezone.now() < limit_record.locked_until:
                    # 仍然在锁定期内
                    remaining_seconds = (limit_record.locked_until - timezone.now()).total_seconds()
                    remaining_minutes = int(remaining_seconds / 60)
                    return False, f'账户已被锁定，请{remaining_minutes}分钟后再试'
                else:
                    # 锁定期已过，解除锁定
                    limit_record.is_locked = False
                    limit_record.locked_until = None
                    limit_record.count = 0
                    limit_record.save()
            
            # 检查失败次数，如果距离上次失败超过30分钟，重置计数
            if (timezone.now() - limit_record.last_visit).total_seconds() > 1800:  # 30分钟
                limit_record.count = 0
                limit_record.save()
        
        return True, max_retry
    
    def _record_login_failed(self, username, ip):
        """记录登录失败"""
        from system.models import SysLimit
        from django.utils import timezone
        
        # 获取配置的最大重试次数
        basic_config = get_object(SysConfig, name='basic')
        max_retry = 5
        if basic_config and basic_config.json_value.get('passwordErrorRetry'):
            max_retry = int(basic_config.json_value.get('passwordErrorRetry', 5))
        
        # 查找或创建限制记录
        limit_record, created = SysLimit.objects.get_or_create(
            username=username,
            limit_key='login',
            defaults={
                'ip': ip,
                'count': 0,
                'last_visit': timezone.now()
            }
        )
        
        # 增加失败次数
        limit_record.count += 1
        limit_record.ip = ip
        limit_record.last_visit = timezone.now()
        
        # 判断是否需要锁定
        if limit_record.count >= max_retry:
            limit_record.is_locked = True
            limit_record.locked_until = timezone.now() + timezone.timedelta(minutes=30)
            logger.warning(f"用户 {username} (来自IP: {ip}) 因密码错误次数过多被锁定30分钟")
        
        limit_record.save()
        logger.info(f"用户 {username} 登录失败，当前失败次数: {limit_record.count}/{max_retry}")
    
    def _clear_login_limit(self, username):
        """清除登录限制记录"""
        from system.models import SysLimit
        SysLimit.objects.filter(username=username, limit_key='login').delete()

    def post(self, request):
        username = request.data.get('username')
        aes_password = request.data.get('password')
        is_authenticated = False
        user = None
        
        # 获取客户端IP
        client_ip = self._get_client_ip(request)
        
        logger.debug("登录请求,用户名:%s,IP:%s" % (username, client_ip))
        
        # 检查登录限制
        is_allowed, message_or_max_retry = self._check_login_limit(username, client_ip)
        if not is_allowed:
            # 账户被锁定
            return Response({'msg': message_or_max_retry, 'token': '', 'data': ''}, status=429)
        
        try:
            # 解密密码
            password = PyCrypt().decrypt(aes_password)
            logger.debug("解密后的密码:%s, 长度:%s" % (password, len(password)))
            # 使用ldap验证用户
            ldap_config = get_object(SysConfig , name='ldap')
            if ldap_config is not None and ldap_config.json_value['enabled'] is True and username != "admin":
                ldap = LdapEntity(ldap_config.json_value)
                if ldap.check_username_password(username, password):
                    # 使用ldap验证成功
                    is_authenticated = True
                    try:
                        user = SysUser.objects.get(username=username)
                    except SysUser.DoesNotExist:
                        # 如果用户不存在，可以创建一个新用户
                        # 从 LDAP 获取更多用户信息
                        ldap_user_info = ldap.fetch_user_info(username)
                        # 创建新用户
                        user = SysUser.objects.create(
                            username=username,
                            # 设置一个随机密码，因为实际认证会通过 LDAP 进行
                            password=make_password(None),  # 需要导入 make_password
                            email=ldap_user_info.get('mail', ''),
                            is_active=True,
                            phonenumber=ldap_user_info.get('mobile', ''),
                            last_login=datetime.now(),
                            user_from=2
                        )
                else:
                    # LDAP验证失败，记录失败
                    self._record_login_failed(username, client_ip)
                    return Response({'msg': 'LDAP用户名密码错误或已禁用', 'token': '', 'data': ''}, status=401)

            if not is_authenticated:
                # 使用Django的认证系统验证用户
                user = authenticate(username=username, password=password, is_active=True)
                
                if user is None:  # 用户不存在或已停用
                    # 记录失败
                    self._record_login_failed(username, client_ip)
                    return Response({'msg': '用户名密码错误或已禁用', 'token': '', 'data': ''}, status=401)
            
            # 登录成功，清除失败记录
            self._clear_login_limit(username)
            
            # 登录用户
            login(request, user)
            # logger.info("用户登录,用户名:%s,用户ID:%s,角色:%s" % (username, user.id, user.roles))
            # 更新最后登录时间
            user.last_login = datetime.now()
            user.save(update_fields=['last_login'])
            
            # 使用Simple JWT生成令牌
            refresh = RefreshToken.for_user(user)
            # 获取访问令牌
            access_token = str(refresh.access_token)
            # 获取刷新令牌
            refresh_token = str(refresh)

            sys_user_roles = SysUserRole.objects.filter(user=user)
            role_list = [role.role for role in sys_user_roles]
            roles = ",".join([role.name for role in role_list])
            sys_role_menus = SysRoleMenu.objects.filter(role__in=role_list)
            menu_set = set([menu.menu for menu in sys_role_menus])
            menu_list = list(menu_set)
            sorted_menu_list = sorted(menu_list)
            # 构造菜单树
            menu_tree = build_tree(sorted_menu_list)
            # 过滤掉按钮类型的菜单，只保留目录和菜单用于前端渲染
            filtered_menu_tree = filter_button_menus(menu_tree)
            serialized_menu_tree = [SysMenuSerializer(menu).data for menu in filtered_menu_tree]
            
            # 提取所有按钮权限，用于前端按钮权限控制
            permissions = []
            def extract_button_perms(menus):
                for menu in menus:
                    if menu.menu_type == 'F' and menu.perms:
                        permissions.append(menu.perms)
                    if hasattr(menu, 'children') and menu.children:
                        extract_button_perms(menu.children)
            extract_button_perms(sorted_menu_list)
            
            logger.debug("过滤后的菜单树:%s", serialized_menu_tree)
            logger.debug("按钮权限列表:%s", permissions)
        except SysUser.DoesNotExist:
            return Response({'msg': '用户名或者密码错误', 'token': None, 'data': None}, status=401)
        
        # 返回令牌和用户数据
        return Response({
            'msg': '登录成功', 
            'token': access_token, 
            'refresh': refresh_token,
            'data': serialized_menu_tree, 
            'permissions': permissions,  # 添加按钮权限列表
            'roles': roles, 
            'user': SysUserSerializer(user).data
        }, status=200)

    def get(self, request):
        token = request.META.get('HTTP_AUTHORIZATION')
        if token != None and token != "":
            print(request.user)
            return Response({'msg': '登录成功','data': token}, status=200)
        else:
            return Response({'msg': '登录失败', 'data': "没有访问权限"}, status=401)

class UserView(ModelViewSet):
    serializer_class = SysUserSerializer
    # permission_classes = [IsOwnerOnly]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['username', 'email', 'phonenumber']
    ordering_fields = ['id', 'username', 'email', 'phonenumber']
    filterset_class = UserFilter
    pagination_class = CommonPagination
    
    def get_queryset(self):
        if has_role_permission_func(self.request, 'admin'):
            return SysUser.objects.all().order_by('id')
        else:
            # 只返回当前登录用户的记录
            return SysUser.objects.filter(id=self.request.user.id).order_by('id')

    @action(detail=False, methods=['post'])
    def modify_password(self, request):
        id = self.request.user.id
        old_password = request.data.get('oldPassword')
        new_password = request.data.get('newPassword')
        logger.debug("修改密码,用户ID:%s,旧密码:%s,新密码:%s" % (id, old_password, new_password))
        try:
            obj_user = SysUser.objects.get(id=id)
            # 检查对象权限，确保用户只能修改自己的密码
            self.check_object_permissions(request, obj_user)
            # ldap开启的话，密码修改需要通过ldap
            ldap_config = get_object(SysConfig , name='ldap')
            if ldap_config is not None and ldap_config.json_value['enabled'] is True:
                LdapEntity(ldap_config.json_value).modify_password(obj_user.username, old_password, new_password)
                logger.info("LDAP修改密码,用户ID:%s,旧密码:%s,新密码:%s" % (id, old_password, new_password))
                return Response({'msg': '修改成功', 'data': ''}, status=200)

            # 使用Django的check_password方法验证原密码
            if obj_user.check_password(old_password):
                logger.info("Django修改密码,用户ID:%s,旧密码:%s,新密码:%s" % (id, old_password, new_password))
                # 使用Django的set_password方法设置新密码
                obj_user.set_password(new_password)
                obj_user.update_time = datetime.now().date()
                obj_user.save()
                return Response({'msg': '修改成功', 'data': ''}, status=200)
            else:
                return Response({'msg': '原密码错误', 'data': ''}, status=400)
        except SysUser.DoesNotExist:
            return Response({'msg': '用户不存在', 'data': ''}, status=404)
        except PermissionDenied:
            return Response({'msg': '无权修改其他用户的密码', 'data': ''}, status=403)

    @action(detail=False, methods=['post'])
    def check(self, request):
        username = request.data.get('username')
        if not username:
            return Response({"msg": "用户名不能为空"}, status=400)
        exists = SysUser.objects.filter(username=username).exists()
        if exists:
            return Response({"msg": "用户名已存在"}, status=400)
        else:
            return Response({"msg": "用户名可用"}, status=200)

    @action(detail=False, methods=['post'])
    def upload_image(self, request):
        file = request.FILES.get('avatar')
        logger.debug("file:", file)
        if file:
            file_name = file.name
            suffix_name = file_name[file_name.rfind("."):]
            new_file_name = datetime.now().strftime('%Y%m%d%H%M%S') + suffix_name
            file_path = str(settings.MEDIA_ROOT) + "/userAvatar/" + new_file_name
            print("file_path:", file_path)
            try:
                with open(file_path, 'wb') as f:
                    for chunk in file.chunks():
                        f.write(chunk)
                return Response({'title': new_file_name}, status=200)
            except:
                return Response({'errorInfo': '上传头像失败'}, status=500)

    @action(methods=['delete'], detail=False, permission_classes=[has_role_permission(['admin'])])
    def batch_delete(self, request):
        ids = request.data.get('ids', [])
        if not ids or not isinstance(ids, list):
            return Response({'msg': '参数错误，需传递用户id列表'}, status=400)
        count, _ = SysUser.objects.filter(id__in=ids).delete()
        return Response({'msg': f'成功删除{count}条用户数据'}, status=200)

    @action(methods=['post'], detail=False, permission_classes=[has_role_permission(['admin'])])
    def grant_role(self, request):
        id = request.data.get('id')
        roleIds = request.data.get('roleIds')
        if roleIds and not isinstance(roleIds, list):
            return Response({'msg': '角色id列表需为列表'}, status=400)
        # 先删除用户已有的角色,如果前端没有选中角色,则不分配
        SysUserRole.objects.filter(user_id=id).delete()
        for role_id in roleIds:
            SysUserRole.objects.create(user_id=id, role_id=role_id)
        return Response({'msg': '分配角色成功'}, status=200)

    # 在用户管理中，分配角色单独写，不要在角色管理中复用，不然造成问题不好查
    @action(methods=['post'], detail=False, permission_classes=[has_role_permission(['admin'])])
    def user_role(self, request):
        id = request.data.get('id')
        roleIds = request.data.get('roleIds')
        if not id or not roleIds:
            return Response({'msg': '参数错误，需传递用户id和角色id列表'}, status=400)
        if not isinstance(roleIds, list):
            return Response({'msg': '角色id列表需为列表'}, status=400)
        # 先删除用户已有的角色,如果前端没有选中角色,则不分配
        SysUserRole.objects.filter(user_id=id).delete()
        for role_id in roleIds:
            SysUserRole.objects.create(user_id=id, role_id=role_id)
        return Response({'msg': '分配角色成功'}, status=200)

    # @has_role_permission_decorator(['admin'])
    # @action(methods=['put'], detail=True)
    def partial_update(self, request, *args, **kwargs):
        # ldap重置密码特殊处理
        ldap_config = get_object(SysConfig , name='ldap')
        if ldap_config is not None and ldap_config.json_value['enabled'] is True:
            id = kwargs.get('pk')
            obj_user = SysUser.objects.get(id=id)
            new_password = request.data.get('password')
            LdapEntity(ldap_config.json_value).reset_password(obj_user.username, new_password)
            logger.info("LDAP重置密码,用户ID:%s,新密码:%s" % (id, new_password))
            return Response({'msg': '重置成功', 'data': ''}, status=200)
        else:
            return super().partial_update(request, *args, **kwargs)

class RoleView(ModelViewSet):
    """
    角色管理
    
    提供角色的增删改查功能
    """
    queryset = SysRole.objects.all().order_by('id')
    serializer_class = SysRoleSerializer
    filter_backends=[DjangoFilterBackend]
    filterset_class = RoleFilter
    pagination_class = CommonPagination
    permission_classes = [has_role_permission(['admin']), MenuBasePermisson]
    
    @swagger_auto_schema(
        operation_summary="批量删除角色",
        operation_description="根据角色ID列表批量删除角色",
        request_body=BatchDeleteSerializer,
        responses={
            200: MessageResponseSerializer,
            400: MessageResponseSerializer
        }
    )
    @action(methods=['delete'], detail=False)
    def batch_delete(self, request):
        ids = request.data.get('ids', [])
        if not ids or not isinstance(ids, list):
            return Response({'msg': '参数错误，需传递角色id列表'}, status=400)
        count, _ = SysRole.objects.filter(id__in=ids).delete()
        return Response({'msg': f'成功删除{count}条角色数据'}, status=200)

    @swagger_auto_schema(
        operation_summary="分配角色菜单",
        operation_description="为指定角色分配菜单权限",
        request_body=GrantMenuSerializer,
        responses={
            200: MessageResponseSerializer,
            400: MessageResponseSerializer
        }
    )
    @action(methods=['post'], detail=False)
    def grant_menu(self, request):
        role_id = request.data.get('role_id')
        menuIds = request.data.get('menuIds')
        if not role_id or not menuIds:
            return Response({'msg': '参数错误，需传递角色id和菜单id列表'}, status=400)
        if not isinstance(menuIds, list):
            return Response({'msg': '菜单id列表需为列表'}, status=400)
        # 先删除用户已有的角色,如果前端没有选中角色,则不分配
        SysRoleMenu.objects.filter(role_id=role_id).delete()
        for menu_id in menuIds:
            SysRoleMenu.objects.create(role_id=role_id, menu_id=menu_id)
        return Response({'msg': '分配角色菜单成功'}, status=200)

class RoleMenuView(ModelViewSet):
    """
    角色菜单关联管理
    
    提供角色与菜单关联的增删改查功能
    """
    queryset = SysRoleMenu.objects.all()
    filter_backends = [DjangoFilterBackend]  # 明确添加过滤器后端
    filterset_class = RoleMenuFilter
    serializer_class = SysRoleMenuSerializer
    permission_classes = [has_role_permission(['admin']), MenuBasePermisson]
    
    @swagger_auto_schema(
        operation_summary="获取角色菜单列表",
        operation_description="根据角色ID获取该角色拥有的菜单ID列表",
        manual_parameters=[
            openapi.Parameter(
                name='role_id',
                in_=openapi.IN_QUERY,
                description='角色ID',
                type=openapi.TYPE_INTEGER,
                required=True
            )
        ],
        responses={
            200: openapi.Response(
                description="成功",
                schema=openapi.Schema(
                    type=openapi.TYPE_OBJECT,
                    properties={
                        'menuIdList': openapi.Schema(
                            type=openapi.TYPE_ARRAY,
                            items=openapi.Schema(type=openapi.TYPE_INTEGER),
                            description='菜单ID列表'
                        )
                    }
                )
            )
        }
    )
    def list(self, request, *args, **kwargs):
        # 获取过滤后的查询集
        queryset = self.filter_queryset(self.get_queryset())
        # 使用序列化器处理数据
        serializer = self.get_serializer(queryset)
        # 直接返回序列化器的数据
        return Response(serializer.data)

class MenuView(ModelViewSet):
    queryset = SysMenu.objects.all()
    serializer_class = SysMenuSerializer
    permission_classes = [has_role_permission(['admin']), MenuBasePermisson]
    
    @action(methods=['get'], detail=False)
    def show_urls(self, request):
        # 获取所有URL模式
        resolver = get_resolver()
        url_set = set()
        
        # 获取自定义排除路径
        exclude_paths = set(settings.MENU_PERM_EXCLUDE_PATHS)

        # 获取所有已配置的API路径
        configured_paths = set()
        menu_items = SysMenu.objects.all()
        for menu in menu_items:
            if menu.api_path:
                # 尝试解析API路径
                try:
                    # 如果是JSON格式
                    paths = json.loads(menu.api_path)
                    if isinstance(paths, list):
                        configured_paths.update(paths)
                        continue
                except (json.JSONDecodeError, TypeError):
                    pass
                
                # 如果是逗号分隔的字符串
                if isinstance(menu.api_path, str):
                    paths = [p.strip() for p in menu.api_path.split(',') if p.strip()]
                    configured_paths.update(paths)

        def _get_patterns(resolver, prefix='/'):
            for pattern in resolver.url_patterns:
                if hasattr(pattern, 'url_patterns'):
                    # 这是一个包含其他URL模式的解析器
                    _get_patterns(pattern, prefix + str(pattern.pattern))
                else:
                    # 这是一个URL模式
                    # view_name = pattern.callback.__name__ if hasattr(pattern.callback, '__name__') else str(pattern.callback)
                    # 获取清理后的URL路径
                    clean_url = _clean_url(prefix + str(pattern.pattern))
                    if clean_url:
                        url_set.add(clean_url)
    
        def _clean_url(url):
            """清理URL模式字符串，移除正则表达式特殊字符"""
            # 移除^和$
            url = url.lstrip('^').rstrip('$')
            
            # 移除可选斜杠 /?
            url = url.replace('/?', '')
            
            # 移除转义字符 \
            url = url.replace('\\', '')
            
            # 移除格式参数部分，如 \.(?P<format>[a-z0-9]+)
            url = re.sub(r'\.\(\?P<[^>]+>[^)]+\)', '', url)
            
            # 移除其他格式参数部分，如 (?P<pk>[^/.]+)
            url = re.sub(r'\(\?P<[^>]+>[^)]+\)', '', url)
            
            # 移除末尾的斜杠
            url = url.rstrip('/')
            

            url = url.replace('^', '')

            # 保留完整路径，不再截取最后一部分
            # 如果末尾有斜杠，添加回来
            if url and not url.endswith('/'):
                url = url + '/'
                
            return url
        
        _get_patterns(resolver)
        
        # 从所有URL中排除已配置的API路径和自定义排除路径
        available_urls = url_set - configured_paths - exclude_paths
        
        # 将集合转为列表并排序
        url_list = sorted(list(available_urls))
        
        # 同时返回已配置的API路径，方便前端展示
        configured_list = sorted(list(configured_paths))
        
        return Response({
            'urls_set': url_set,
            'urls': url_list,
            'configured_urls': configured_list
        }, status=200)

class MenuTreeView(APIView):
    """
    构建菜单树
    
    提供获取系统菜单树的接口
    """
    def get(self, request):
        try:
            menu_list = SysMenu.objects.order_by("order_num")
            menu_tree = build_tree(menu_list)
            serialized_menu_tree = [SysMenuSerializer(menu).data for menu in menu_tree]
            logger.info("菜单树数据:%s" % json.dumps(serialized_menu_tree, ensure_ascii=False, indent=2))
            return Response({'msg': '获取成功', 'menu_tree': serialized_menu_tree})
            
        except Exception as e:
            return Response({'msg': str(e), }, status=500)

class SysConfigView(APIView):
    permission_classes = [has_role_permission(['admin']), MenuBasePermisson]

    def get(self, request, *args, **kwargs):
        """处理获取配置请求"""
        config_type = request.query_params.get('name', None)
        if config_type:
            # 尝试获取指定类型的配置
            config = SysConfig.objects.filter(name=config_type).first()
            if config:
                # 如果找到配置，返回单个对象
                serializer = SysConfigSerializer(config)
                return Response(serializer.data)
            else:
                # 如果未找到，创建一个空配置
                return Response({
                    'name': config_type,
                    'json_value': '{}',
                    'remark': f'{config_type} 配置'
                })
        else:
            return Response({'msg': '未指定配置类型'}, status=400)
        # 如果没有指定类型，使用默认的列表行为
        return super().list(request, *args, **kwargs)


    def post(self, request, *args, **kwargs):
        """处理创建配置请求
        
        支持提交单个配置项或配置项列表
        """
        data = request.data
        config_type = request.query_params.get('name', None)
        if not config_type:
            return Response({'msg': '未指定配置类型'}, status=400)  
        
        # 如果是测试连接请求
        if 'test' in request.query_params and config_type == 'ldap':
            from utils.CommonUtils import LdapEntity
            
            # 直接使用前端提交的数据进行测试
            ldap_config = data
            print("测试LDAP连接数据:", ldap_config)
            print("=============================")
                
            # 测试LDAP连接
            success, result = LdapEntity(ldap_config).test_connection()
            if success:
                return Response({'success': True, 'message': '连接成功', 'data': str(result)})
            else:
                return Response({'success': False, 'message': f'连接失败: {result}'})
        
        # 如果是立即同步请求
        if 'sync' in request.query_params and config_type == 'ldap':
            from utils.CommonUtils import LdapEntity
            
            # 获取同步参数
            import_ou = data.get('import_ou')
            sync_disabled = data.get('sync_disabled', True)
            sync_existing = data.get('sync_existing', True)
            
            # 获取LDAP配置
            ldap_config = get_object(SysConfig, name='ldap')
            if not ldap_config or not ldap_config.json_value.get('enabled'):
                return Response({'success': False, 'message': 'LDAP未启用或配置不存在'}, status=400)
            
            # 执行同步
            ldap = LdapEntity(ldap_config.json_value)
            success_count, fail_count, details = ldap.sync_users(
                import_ou=import_ou,
                sync_disabled=sync_disabled,
                sync_existing=sync_existing
            )
            
            return Response({
                'success': True,
                'message': f'同步完成: 成功 {success_count} 个, 失败 {fail_count} 个',
                'success_count': success_count,
                'fail_count': fail_count,
                'details': details[:100]  # 只返回前100条详细信息
            })
        
        # 如果是获取OU列表请求
        if 'ou_list' in request.query_params and config_type == 'ldap':
            from utils.CommonUtils import LdapEntity
            
            # 获取LDAP配置
            ldap_config = get_object(SysConfig, name='ldap')
            if not ldap_config or not ldap_config.json_value.get('enabled'):
                return Response({'success': False, 'message': 'LDAP未启用或配置不存在'}, status=400)
            
            # 获取OU列表
            ldap = LdapEntity(ldap_config.json_value)
            ou_list = ldap.get_ou_list()
            
            return Response({
                'success': True,
                'data': ou_list
            })
        
        # 创建
        else:
            if isinstance(data, (dict, list)):
                json_value = data
            else:
                json_value = json.loads(data)
            
            # 处理LDAP配置的定时同步任务
            if config_type == 'ldap' and json_value.get('sync_enabled') and json_value.get('task_definition_id'):
                # 通过选择的任务定义创建或更新LDAP同步任务
                from tasks.models import TaskDefinition, Tasks
                
                task_definition_id = json_value.get('task_definition_id')
                
                try:
                    # 获取任务定义
                    task_def = TaskDefinition.objects.get(id=task_definition_id)
                    import_ou = json_value.get('import_ou')
                    assert import_ou, "import_ou不能为空"
                    # 构造任务参数
                    task_kwargs = {
                        'import_ou': import_ou,
                        'sync_disabled': json_value.get('sync_disabled', True),
                        'sync_existing': json_value.get('sync_existing', True)
                    }
                    tasks_name = f'LDAP同步任务-{import_ou}'
                    # 查找或创建LDAP同步任务（一个任务定义只对应一个LDAP同步任务）
                    task, task_created = Tasks.objects.get_or_create(
                        name=tasks_name,
                        defaults={
                            'task_definition': task_def,
                            'task_kwargs': task_kwargs,
                            'task_func': 'ldap_sync_users_task',
                            'enabled': True,
                            'description': 'LDAP用户定时同步任务',
                            'creator': request.user,
                            'updater': request.user
                        }
                    )
                    
                    # 如果任务已存在，更新参数
                    if not task_created:
                        task.task_kwargs = task_kwargs
                        task.enabled = True
                        task.updater = request.user
                        task.save()
                    
                    logger.info(f"LDAP同步任务{'[创建]' if task_created else '[更新]'}成功: {task.id}, 任务定义: {task_def.name}")
                    
                except TaskDefinition.DoesNotExist:
                    logger.error(f"任务定义ID {task_definition_id} 不存在")
                    return Response({'success': False, 'message': '选择的任务定义不存在'}, status=400)
            
            # 如果LDAP同步被禁用，禁用所有LDAP同步任务
            elif config_type == 'ldap' and not json_value.get('sync_enabled'):
                from tasks.models import Tasks
                try:
                    # 禁用所有LDAP同步任务
                    Tasks.objects.filter(task_func='ldap_sync_users_task').update(enabled=False)
                    logger.info("LDAP同步任务已禁用")
                except Exception as e:
                    logger.error(f"禁用LDAP同步任务失败: {str(e)}")
            
            obj, created = SysConfig.objects.update_or_create(
                name=config_type,
                defaults={
                    'json_value': json_value,
                    'remark': f'{config_type} 配置'
                }
            )
            if created:
                return Response({'success': True, 'message': '配置创建成功'})
            return Response({'success': True, 'message': '配置更新成功'})
            
class SysOperLogView(ModelViewSet):
    """操作日志记录"""
    queryset = SysOperLog.objects.all()
    serializer_class = SysOperLogSerializer
    permission_classes = [has_role_permission(['admin']), MenuBasePermisson]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ['title', 'business_type', 'status', 'oper_name', 'oper_url']
    search_fields = ['title', 'oper_name', 'oper_ip', 'oper_url']
    ordering_fields = ['oper_time', 'business_type']
    pagination_class = CommonPagination  # 使用自定义分页

    @action(methods=['delete'], detail=False)
    def clean(self, request):
        """清空操作日志"""
        SysOperLog.objects.all().delete()
        return Response({'msg': '清空成功'})
        
    @action(methods=['post'], detail=False)
    def batch_delete(self, request):
        """批量删除操作日志"""
        ids = request.data.get('ids', [])
        SysOperLog.objects.filter(id__in=ids).delete()
        return Response({'msg': '删除成功'})